home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Network Supervisor's Toolkit
/
Network Supervisor's Toolkit.iso
/
tools
/
tsrc29
/
chkfrag.c
next >
Wrap
Text File
|
1996-07-10
|
26KB
|
846 lines
/***************************************************
* *
* chkfrag - check disk for fragmentation *
* *
***************************************************/
#include <stdio.h> /* standard library */
#include <dos.h> /* dos access and registers */
#include <malloc.h> /* memory allocation */
#include <stdlib.h> /* common lib modules */
#define UINT unsigned int /* unsigned integer type */
#define ULONG unsigned long /* unsigned long type */
#define NOT ! /* logical not */
#define BOOT b_rec /* boot record shorthand */
#define FILES (_A_SYSTEM | _A_HIDDEN | _A_SUBDIR) /* files type */
#define LABEL (_A_VOLID) /* label type */
#define CLEAR(s,c) strclr(s,c,sizeof(s)) /* string clear */
/*
* Globals
*/
char huge *fat, /* address of FAT */
cdrive[66], /* startup drive and path */
fat_16; /* true if 16 bit FAT entries */
int sections = 0, /* file sections */
secsize = 0 , /* sector size of drive */
frag = 0, /* fragmented files */
unfrag = 0, /* unfragmented files */
files = 0, /* processed files */
dirs = 0, /* processed directories */
list = 0; /* list frag'd files switch */
UINT nclusters; /* number of clusters */
/*
* formal declarations
*/
void get_fat(int), /* read FAT into memory */
cfexit(int), /* exit routine */
check_frag(char *, UINT, int), /* check if file/dir is frag'd */
dir_search(char *), /* search directory */
strclr(char *, int, int), /* clear a string */
check_unlinked(); /* check for unlinked clusters */
int chkdrv(char); /* check local, SUBST/ASSIGN */
char *fname(char *, char*), /* fcb filename to normal fname */
*readlabel(char), /* read the drive label */
*translate_name(char far *); /* translate name */
UINT next_cluster(UINT, int, int *); /* find the next cluster in FAT */
struct fcb
{
char hexff; /* extended fcb first byte */
char extra[5]; /* extended fcb work area */
char attrib; /* extended fcb attribute */
char drive; /* fcb - drive */
char filename[8]; /* fcb - filename */
char ext[3]; /* fcb - extension */
unsigned
int block; /* fcb - block number */
unsigned
long filesize; /* fcb - file size */
int date; /* fcb - file date */
char system[10]; /* fcb - reserved area */
char record; /* fcb - current record */
unsigned
long rnd_recno; /* fcb - random record number */
} ;
/************************************************************************
* *
* mainline *
* *
************************************************************************/
main(argc, argv)
int argc; /* count of arguments */
char *argv[]; /* argument strings */
{
int pf = 0, /* percent fragmented */
ctc = 0, /* return code chosen */
dc = 0, /* drive chosen */
pe = 0, /* parm in error */
rc, /* return code */
i, j, /* loop counter, work */
option = 0; /* program option */
char *p; /* work pointer */
static
char drive[] = " :\\", /* drive and path to check */
*rc_type[] =
{ "Percentage",
"Number of Files",
"Number of Extra Segments" },
*suggestion[] =
{ "No fragmentation -- No action suggested",
"Little fragmentation -- No action suggested",
"Moderate fragmentation -- Defrag should be performed soon",
"Fragmentation critical -- Defrag or Backup/Format/Restore" },
*errors[] =
{ "Invalid drive specified",
"Cannot CHKFRAG a network drive",
"Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
"Must run with DOS 2.0 or greater" },
*options[] =
{ "/%", "/N", "/E", "/L" } ;
printf("CHKFRAG 1.0 (c) Ziff Communications Co.\n%s%c%s\n",
"PC Magazine ", 254, " Bob Flanders & Michael Holmes\n");
getcwd(cdrive, sizeof(cdrive)); /* get current drive/path */
*drive = *cdrive; /* ..setup default drive */
for (i = 1; i < argc; i++) /* check each argument */
{
strupr(p = argv[i]); /* uppercase argument */
if (strlen(p) == 2 && p[1] == ':') /* q. drive parm specified? */
{
*drive = *p; /* a. yes .. setup drive */
dc++; /* .. show drive selected */
}
else
{
for (j = 0; strcmp(p, options[j]) /* search arguments */
&& (j < 4); j++);
switch(j) /* based on argument */
{
case 0: /* /% option */
case 1: /* /N option */
case 2: /* /E option */
option = j; /* set up the option value */
ctc++; /* increment code type count*/
break; /* exit switch */
case 3: /* /L switch */
list++; /* .. show listing wanted */
break;
case 4: /* error */
pe = j; /* argument in error */
break; /* .. error */
}
}
}
if (pe || (ctc > 1) || (list>1) || (dc > 1))/* q. any error? */
{ /* a. yes .. handle error */
printf("\n\tformat\tCHKFRAG [d:] [/%% | /N | /E] [/L]\n\n");
printf("\twhere\td: is the drive to check for fragmentation\n");
printf("\t\t/%% sets errorlevel as a percentage\n");
printf("\t\t/N sets errorlevel to number of fragmented files (max 254)\n");
printf("\t\t/E sets errorlevel number of extra sections (max 254)\n");
printf("\t\t/L causes fragmented files to be listed\n");
cfexit(255);
}
if (i = chkdrv(*drive)) /* check drive, version err */
{
printf("Error: %s", errors[--i]); /* display any error */
cfexit(255); /* tell the batch job */
}
get_fat(*drive - 'A'); /* read FAT into memory */
dir_search(drive); /* search for files */
check_unlinked(); /* check unlinked clusters */
if (files + dirs) /* q. any files and dirs? */
pf = (frag * 100) / (files + dirs); /* a. yes .. % files frag'd */
if (!pf && frag) /* q. something frag'd */
pf = 1; /* a. yes .. show non-zero */
printf("\n%d Files, %d Directories,\n", /* report to user */
files, dirs);
printf("%d Unfragmented, %d Fragmented, %d Extra Sections\n",
unfrag, frag, sections);
printf("%d%% of files are fragmented\n\n", pf);
switch(option) /* return w/errorlevel */
{
case 0: /* percentage return */
rc = pf;
break;
case 1: /* files return */
rc = frag > 254 ? 254 : frag;
break;
case 2: /* extra sections return */
rc = sections > 254 ? 254 : sections;
}
if (pf == 0) /* q. no fragments? */
i = 0; /* a. yes .. tell 'em */
else if (pf < 11) /* q. little fragmentation? */
i = 1; /* a. yes .. set index */
else if (pf < 76) /* q. moderate fragm'tion */
i = 2; /* a. yes .. setup msg */
else
i = 3; /* ..push the button, Jim */
printf("%s%s\nCHKFRAG finished, Return code %d\n\n%s%s\n",
"Return type chosen: ", rc_type[option], rc,
"Suggestion:\n ", suggestion[i]);
cfexit(rc); /* return w/errorlevel */
}
/************************************************************************
* *
* get_fat -- read boot record and fat into memory *
* *
************************************************************************/
void get_fat(drv)
int drv; /* drive number */
{
union REGS r; /* work registers */
struct SREGS s; /* ..and work segment regs */
struct bootrec
{
char jmp[3], /* jump instruction */
oem[8]; /* OEM name */
UINT bytes; /* bytes per sector */
char cluster; /* sectors per cluster */
UINT res_sectors; /* reserved sectors */
char fats; /* number of fats */
UINT roots, /* number of root dir entries */
sectors; /* total sectors */
char media; /* media descriptor block */
UINT fatsize, /* sectors per fat */
tracksize, /* sectors per track */
heads, /* number of heads */
hidden; /* hidden sectors */
} far *b_rec; /* boot record definition */
char *nomem = "Not enough memory for processing\n";
r.h.ah = 0x36; /* ah = get freespace */
r.h.dl = drv + 1; /* get drive */
int86(0x21, &r, &r); /* r.x.cx = bytes/sector */
if ((BOOT = (struct bootrec far *) malloc(r.x.cx)) == NULL)
{ /* q. no memory? */
printf(nomem); /* a. yes .. give */
cfexit(255); /* ..error msg/exit */
}
r.h.al = drv; /* al = drive number */
r.x.cx = 1; /* cx = number of sectors */
r.x.dx = 0; /* dx = starting sector */
r.x.bx = FP_OFF(BOOT); /* bx = offset of buffer */
s.ds = FP_SEG(BOOT); /* ds = segment of buffer */
int86x(0x25, &r, &r, &s); /* read boot sector */
if (r.x.cflag) /* q. error reading disk? */
{
printf("Error reading boot record\n"); /* a. yes .. give error msg */
cfexit(255); /* ..and return to DOS */
}
if ((fat = (char huge *) halloc((long) BOOT->fatsize * (long) BOOT->bytes, 1))
== (char huge *) NULL)
{ /* q. no memory? */
printf(nomem); /* a. yes .. give */
cfexit(255); /* ..error msg/exit */
}
fat_16 = (BOOT->sectors / BOOT->cluster) > 4087;/* set if 16bit FAT tbl */
r.h.al = drv; /* al = drive number */
r.x.cx = BOOT->fatsize; /* cx = number of sectors */
r.x.dx = BOOT->res_sectors; /* dx = starting sector */
r.x.bx = FP_OFF(fat); /* bx = offset of buffer */
s.ds = FP_SEG(fat); /* ds = segment of buffer */
int86x(0x25, &r, &r, &s); /* read boot sector */
if (r.x.cflag) /* q. error reading disk? */
{
printf("%02.2x %02.2x Error reading FAT\n",
r.h.ah, r.x.di); /* a. yes .. give error msg */
cfexit(255); /* ..and return to DOS */
}
nclusters = (BOOT->sectors - (BOOT->res_sectors
+ (BOOT->fatsize * BOOT->fats)
+ ((BOOT->roots * 32) / BOOT->bytes)))
/ BOOT->cluster;
printf("Drive %c:%s %u Sectors, %u Clusters, %u Clustersize\n",
drv + 'A', readlabel(drv + 'A'), BOOT->sectors, nclusters,
BOOT->cluster * BOOT->bytes);
printf("\nChecking disk structure ..");
}
/************************************************************************
* *
* check_frag -- check a file/directory for fragmentation *
* *
************************************************************************/
void check_frag(s, n, dflag)
char *s; /* file/directory name */
UINT n; /* starting cluster number */
int dflag; /* directory flag */
{
UINT i, j; /* working storage */
int flag = 0, /* flag for frag'd file */
rc; /* error return code */
for(; i = next_cluster(n, 1, &rc); n = i) /* walk down the chain */
{
if (i == 1) /* q. invalid cluster? */
{
printf("\n\t%s -- %s%s\n%s\n", /* a. yes .. give err msg */
s, rc ? "Invalid cluster detected"
: "File cross-linked",
", Run aborted",
"\n\t** Please run CHKDSK **");
cfexit(255); /* ..and exit w/error code */
}
if ((n + 1) != i) /* q. non-contiguous area? */
{
flag++; /* show fragmented file */
if (i > n) /* q. possibly bad cluster? */
{
for (j = n + 1; /* check for bad spots */
next_cluster(j, 0, &rc) == 0xfff7 && j < i;
j++);
if (j == i) /* q. was entire area bad? */
flag--; /* a. yes .. don't report */
else
sections++; /* incr files sections count*/
}
else
sections++; /* incr files sections count*/
}
}
if (flag) /* q. fragmented file */
{
if (NOT frag && list) /* q. first frag file? */
printf("\nFragmented Files/Directories:\n");
if (list) /* q. list frag'd files? */
printf("%s%s\n", /* a. yes .. give it to them*/
dflag ? "DIR> " : " ", s);
frag++; /* accumulate frag'd count */
}
else
unfrag++; /* else total unfrag'd files*/
}
/************************************************************************
* *
* next_cluster -- return next cluster number from FAT *
* *
************************************************************************/
UINT next_cluster(n, x, rc)
UINT n; /* current cluster number */
int x, /* flag, 1 = reset FAT entry */
*rc; /* error return code */
{
ULONG e; /* entry number in FAT */
UINT huge *p, /* pointer for 16 bit FAT entry */
mask1, mask2; /* mask for and'ing and or'ing */
int flag; /* shift/and flag */
*rc = 0; /* clear return code */
if (! (e = n)) /* q. invalid cluster nbr */
return(0); /* a. yes .. rtn EOF */
if (fat_16) /* q. 16 bit FAT entries? */
{
p = (UINT *) &fat[0]; /* a. yes .. get FAT addr */
n = p[e]; /* retrieve next entry */
if (NOT n) /* q. unallocated cluster? */
{
n = 1; /* a. yes .. error condition*/
*rc = 1; /* set return code */
}
if (x) /* q. need to reset entry? */
p[e] = 1; /* a. yes .. show processed */
if (n >= 0xfff0 && n != 0xfff7) /* q. reserved and not bad */
n = 0; /* a. yes .. show EOF */
}
else
{
e = (n << 1) + n; /* cluster number * 3 */
flag = e & 1; /* need to do shift later? */
e >>= 1; /* cluster number * 1.5 */
n = *(UINT *) &fat[e]; /* get next cluster */
if (flag) /* q. need to do shift? */
{
n >>= 4; /* a. yes .. shift by 4 bits*/
mask1 = 0x000f; /* mask to clear upper bits */
mask2 = 0x0010; /* ..and footprint mask */
}
else
{
n &= 0xfff; /* else .. strip upper bits */
mask1 = 0xf000; /* mask to clear lower bits */
mask2 = 0x0001; /* ..and footprint mask */
}
if (NOT n) /* q. unallocated cluster? */
{
n = 1; /* a. yes .. error condition*/
*rc = 1; /* set return code */
}
if (x) /* q. need to reset entry? */
{
*(UINT *) &fat[e] &= mask1; /* a. yes .. 'and' off bits */
*(UINT *) &fat[e] |= mask2; /* ..and put down footprint */
}
if (n >= 0xff0) /* q. EOF/reserved range? */
if (n == 0xff7) /* q. bad cluster? */
n = 0xfff7; /* a. yes .. show bad one */
else
n = 0; /* else .. show EOF */
}
return(n);
}
/************************************************************************
* *
* check_unlinked -- check for unlinked clusters *
* *
************************************************************************/
void check_unlinked()
{
int rc; /* error return code */
UINT i, /* loop counter */
j; /* work return cluster nbr */
for (i = 2; i < nclusters; i++) /* check thru entire FAT */
{
if ((j = next_cluster(i, 0, &rc)) != 0 /* q. unallocated cluster? */
&& j != 1 && j != 0xfff7) /* ..or used/bad cluster? */
{
printf("\nLost clusters detected, %s%s",/* a. no .. give msg */
"Run aborted\n",
"\t** Please run CHKDSK **\n");
cfexit(255); /* ..and exit w/error */
}
}
}
/************************************************************************
* *
* dir_search -- recursively search all files & subdirectories *
* *
************************************************************************/
void dir_search(base_dir)
char *base_dir; /* base subdirectory to search */
{
int oldds, /* old dta segment */
oldda; /* old dta address */
char pass, /* pass number */
work_dir[65], /* work directory */
first_done; /* find first done */
struct fcb find_work; /* fcb work area */
/*
* The following areas are STATIC .. not allocated on recursion
*/
static
struct SREGS s; /* segment registers */
static
union REGS r; /* other registers */
static
char far *cftmp; /* work pointer */
static
int rc; /* work return code */
static
union
{
char dtabuff[128]; /* dta area */
struct /* Disk transfer area layout */
{
char dta1[6]; /* first part of dta */
char attrib; /* attribute byte */
char drive; /* drive */
char filename[8]; /* filename */
char ext[3]; /* extension */
char d_attrib; /* directory attribute */
char dta2[10]; /* more reserved space */
unsigned
int d_time; /* directory time */
unsigned
int d_date; /* directory date */
unsigned
int d_cluster; /* first cluster */
unsigned
long d_filesize; /* size of file */
} dta;
} dta;
/*
* End of static area
*/
r.h.ah = 0x2f; /* ah = get dta */
int86x(0x21, &r, &r, &s); /* .. ask DOS */
oldds = s.es; /* save old DTA segment */
oldda = r.x.bx; /* .. and offset */
cftmp = (char far *) &dta; /* get current fcb address */
r.h.ah = 0x1a; /* ah = set DTA */
s.ds = FP_SEG(cftmp); /* ds -> DTA segment */
r.x.dx = FP_OFF(cftmp); /* ds:dx -> DTA */
int86x(0x21, &r, &r, &s); /* setup new DTA */
if (strcmp(base_dir, translate_name(base_dir))) /* q. JOIN'd? */
return; /* a. yes .. skip it */
chdir(base_dir); /* get the base directory */
for(first_done=pass=0;;) /* look through current dir */
{
if (first_done == 0) /* q. find first done? */
{ /* a. no .. do it */
if (base_dir[1] == ':') /* q. disk specified? */
find_work.drive = /* a. yes .. set fcb drive */
((base_dir[0] & 0xdf) - 'A') + 1;
else
find_work.drive = 0; /* else use default */
find_work.hexff = 0xff; /* extended fcb */
CLEAR(find_work.extra, 0); /* set extra area */
CLEAR(find_work.filename, '?'); /* .. and file name */
CLEAR(find_work.ext, '?'); /* .. and extension */
find_work.attrib = FILES; /* set up attribute to find */
r.h.ah = 0x11; /* ah = find first */
cftmp = (char far *) &find_work; /* get pointer to work fcb */
s.ds = FP_SEG(cftmp); /* ds -> segment of fcb */
r.x.dx = FP_OFF(cftmp); /* ds:dx -> offset */
int86x(0x21, &r, &r, &s); /* .. find first */
rc = r.h.al; /* get return code */
first_done = 1; /* first find done */
}
else
{
r.h.ah = 0x12; /* ah = find next */
cftmp = (char far *) &find_work; /* get pointer to work fcb */
s.ds = FP_SEG(cftmp); /* ds -> segment of fcb */
r.x.dx = FP_OFF(cftmp); /* ds:dx -> offset */
int86x(0x21, &r, &r, &s); /* .. find first */
rc = r.h.al; /* get return code */
}
strcpy(work_dir, base_dir); /* get current base */
if (work_dir[strlen(work_dir)-1] != '\\') /* if needed .. */
strcat(work_dir, "\\"); /* .. add a backslash */
strcat(work_dir, /* .. add the name */
fname(dta.dta.filename, dta.dta.ext));
if (pass) /* q. second pass? */
{
if (rc) /* q. more files found? */
break; /* a. no .. exit */
if (!(dta.dta.d_attrib & _A_SUBDIR) /* q. directory? */
|| (dta.dta.filename[0] == '.')) /* .. or a dot dir? */
continue; /* a. get next entry */
dirs++; /* accumulate dir count */
dir_search(work_dir); /* recursively call ourself */
}
else /* first pass processing */
{
if (rc) /* q. anything found? */
{ /* a. no .. */
first_done = 0; /* re-execute find-first */
pass++; /* go to next pass */
continue; /* .. continue processing */
}
if (dta.dta.filename[0] == '.') /* q. dot directory? */
continue; /* a. yes .. skip it */
if (!(dta.dta.d_attrib & _A_SUBDIR)) /* q. a file? */
files++; /* a. yes .. count them */
check_frag(work_dir, /* check for frag'd file */
dta.dta.d_cluster,
dta.dta.d_attrib & _A_SUBDIR);
}
}
r.h.ah = 0x1a; /* ah = set DTA */
s.ds = oldds; /* ds -> DTA segment */
r.x.dx = oldda; /* ds:dx -> DTA */
int86x(0x21, &r, &r, &s); /* setup new DTA */
}
/************************************************************************
* *
* fname -- build a normalized filename from an FCB *
* *
************************************************************************/
char *fname(filename, ext)
char *filename; /* filename with trailing blanks*/
char *ext; /* extension */
{
int i; /* loop control */
char *p; /* work pointer */
static
char fwork[13]; /* returned work area */
p = fwork; /* initialize string pointer*/
for (i = 0; (i < 8) && (*filename != ' '); i++) /* move fname w/o blanks*/
*p++ = *filename++;
if (*ext != ' ') /* q. extension blank? */
{
*p++ = '.'; /* a. no .. add the dot */
for (i = 0; (i < 3) && (*ext != ' '); i++) /* add ext w/o blanks */
*p++ = *ext++;
}
*p = 0; /* terminate string w/null */
return(fwork); /* return string to caller */
}
/************************************************************************
* *
* strclr -- clear an area to a value *
* *
************************************************************************/
void strclr(s, c, n)
char *s; /* area to initialize */
int c, /* value to use */
n; /* length of area to clear */
{
while(n--) /* initialize whole area */
*s++ = c; /* ..to value specified */
}
/************************************************************************
* *
* translate_name -- translate a DOS name *
* *
************************************************************************/
char *translate_name(name)
char far *name; /* name to translate */
{
static
char translate_area[65], /* work/return area */
far *sp; /* work pointer */
union REGS r; /* work registers */
struct SREGS s; /* ..and work segment regs */
r.h.ah = 0x60; /* ah = translate */
sp = (char far *) name; /* set up a pointer .. */
r.x.si = FP_OFF(sp); /* set pointer to input name */
s.ds = FP_SEG(sp); /* .. and segment */
sp = (char far *) translate_area; /* set up a pointer .. */
r.x.di = FP_OFF(sp); /* set pointer to output area */
s.es = FP_SEG(sp); /* .. and segment */
int86x(0x21, &r, &r, &s); /* translate the name */
if (r.x.cflag) /* if bad name .. */
return((char far *) NULL); /* .. return error */
else
return(translate_area); /* return xlated name */
}
/************************************************************************
* *
* chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd *
* *
************************************************************************/
int chkdrv(c)
char c; /* drive to check */
{
union REGS r; /* work registers */
struct SREGS s; /* ..and work segment regs */
static
char wdrv[] = " :\\"; /* work area for drive name */
if (_osmajor < 2) /* q. pre-DOS 2.00? */
return(4); /* a. yes .. can't run it */
if (_osmajor >= 3 && _osminor >= 1) /* q. DOS 3.1 or higher? */
{
r.x.ax = 0x4409; /* ah = ioctl, local test */
r.h.bl = (c - 'A') + 1; /* bl = drive to test */
int86(0x21, &r, &r); /* test drive */
if (r.x.cflag) /* q. bad drive? */
return(1); /* a. yes .. error */
if (r.x.dx & 0x1000) /* q. remote? */
return(2); /* a. yes .. error */
wdrv[0] = c; /* set up name */
if (strcmp(wdrv, translate_name(wdrv))) /* q. SUBST or ASSIGNED? */
return(3); /* a. yes .. return error */
}
return(0); /* return ok */
}
/************************************************************************
* *
* Read drive label, if available *
* *
************************************************************************/
char *readlabel(c)
char c; /* drive to check */
{
char *p, *q; /* work pointers */
struct find_t f; /* structure for directory entry*/
static
char work_dir[13] = { " :\\*.*" } ; /* directory to check */
work_dir[0] = c; /* setup for find first */
if (_dos_findfirst(work_dir, LABEL, &f)) /* q. error on label get? */
work_dir[0] = 0; /* a. yes .. then no label */
else
{
for(p = work_dir, q = f.name; *q; q++) /* copy label w/o middle . */
if (*q != '.') /* q. is this char a dot? */
*p++ = *q; /* a. no .. copy it */
*p = 0; /* terminate string */
}
return(work_dir); /* ..and return label string*/
}
/************************************************************************
* *
* cfexit() - return to DOS after resetting dir, dir *
* *
************************************************************************/
void cfexit(rc)
int rc; /* return code to exit with */
{
int i; /* work variable */
_dos_setdrive((*cdrive - 'A')+1, &i); /* reset the default drive */
chdir(cdrive); /* .. and directory */
exit(rc); /* .. and return to DOS */
}